using System;
using Tycho.Parser.Peg;

namespace Tycho.Grammar {
    class TermMacroCompiler : IProductionCompiler {
        private readonly INonTerminalRuleCompiler NonTerminalRuleCompiler;
        private readonly ITermMacroBuilderCompiler TermMacroBuilderCompiler;
        private readonly Func<NonTerminal> CreateNonTerminal;
        private readonly INonTerminalBuilder NonTerminalBuilder;

        public TermMacroCompiler(INonTerminalRuleCompiler ruleCompiler)
            : this (
            ruleCompiler,
            new TermMacroBuilderCompiler (),
            () => new NonTerminal (),
            new NonTerminalBuilder ()) {}

        public TermMacroCompiler (INonTerminalRuleCompiler nonTerminalRuleCompiler, ITermMacroBuilderCompiler termMacroBuilderCompiler, Func<NonTerminal> createNonTerminal, INonTerminalBuilder nonTerminalBuilder) {
            NonTerminalRuleCompiler = nonTerminalRuleCompiler;
            TermMacroBuilderCompiler = termMacroBuilderCompiler;
            CreateNonTerminal = createNonTerminal;
            NonTerminalBuilder = nonTerminalBuilder;
        }

        public GrammarTree Compile (ITerm term, ChoicePrecedence precedence, ProductionCompilerContext context) {
            var compositeTerm = (CompositeTerm) term;

            ITerm rule = compositeTerm.SubTerms["rule"];
            ITerm termTransform = compositeTerm.SubTerms["term"];

            var nonTerminal = CreateNonTerminal ();

            context.DeferredCompilations.AddCompilationStep (nonTerminal,
                 () => CompileNonTerminal (nonTerminal, rule, precedence, termTransform, context));

            return new GrammarTree (nonTerminal);
        }

        private void CompileNonTerminal (NonTerminal nonTerminal, ITerm rule, ChoicePrecedence precedence, ITerm termTransform, ProductionCompilerContext context) {
            RuleCaptures ruleCaptures = NonTerminalRuleCompiler.Compile (rule, context);

            NonTerminalBuilder.Build (null, nonTerminal, ruleCaptures);

            ITermMacroBuilder termBuilder = TermMacroBuilderCompiler.Compile (termTransform);

            nonTerminal.ResultTransform = r => {
                                              var compositeTerm = (CompositeTerm) r.Term;
                                              r.Term = termBuilder.BuildTerm (compositeTerm.SubTerms);
                                              return r;
                                          };
        }
    }
}